wei554665488360360<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2017/10/12
 * Time: 17:35
 */

/**
 * Class mould
 * @User: yfl
 * @Time:2017年10月12日 17:37:03
 * @description:PHP通过反射动态加载第三方类和获得类源码的实例
 * @from:http://www.jb51.net/article/75539.htm
 * 用反射加载第三方类用处在于：
 * 1.使用xml或者其他配置文件配置要加载的类，从而和系统的源代码分离
 * 2.对加载的类进行检查，使加载的类符合自己定义的结构.
 */
echo '<pre>';

abstract class Module
{
    public function baseFunc()
    {
        echo 'I am baseFun';
    }

    abstract public function execute();
}


class ModuleRunner
{
    //模拟xml配置，动态配置需要加载的Module
    private $configData = array(
        'PersonModule' => array('person' => 'bob'),
        'FtpModule' => array('host' => 'example.com', 'user' => 'admin')
    );
    private $modules = array();

    /**
     * @User: yfl
     * @Time:2017年10月12日 18:33:22
     * @description:初始化moduleRunner 加载配置的module
     */
    public function init()
    {
        $parent = new ReflectionClass('Module');
        foreach ($this->configData as $moduleName => $param) { //#检查配置中的Module是否合法
            $ReflectionModuleClass = new ReflectionClass($moduleName);
            if (!$ReflectionModuleClass->isSubclassOf($parent)) {  #检查是否是Module的子类型
                throw new Exception("unknown type : {$moduleName}");
            }
            $moduleObj = $ReflectionModuleClass->newInstance();
            foreach ($ReflectionModuleClass->getMethods() as $method) {  #检查配置中的函数的参数格式是否正确
                $this->handleMethod($moduleObj, $method, $param);
            }
            array_push($this->modules, $moduleObj);
        }

    }

    /**
     * @param Module $module :实例化的对象
     * @param ReflectionMethod $method : 反射类获得类的方法
     * @param $param
     * @return bool
     * @User: yfl
     * @Time: 2017年10月13日 14:38:31
     * @description:
     */
    private function handleMethod(Module $moduleObj, ReflectionMethod $method, $param)
    {
        $methodName = $method->getName();
        $args = $method->getParameters();
        if (count($args) != 1 || substr($methodName, 0, 3) != 'set') {
            return false; #如果没有配置中的类的方法的参数个数不为1,或者方法名前3个字母不为set,返回false
        }
        $property = strtolower(substr($methodName, 3));
        if (!isset($param[$property])) {
            return false;  //#如果方法名后三个字母与配置中的参数名不同,返回false
        }

        $argClass = $args[0]->getClass();

        if (empty($argClass)) {
            $method->invoke($moduleObj, $param[$property]);
        } else {
//            var_dump($argClass);
            $method->invoke($moduleObj, $argClass->newInstance($param[$property]));   #有类型限制则新建一个实例并调用set方法
        }
    }

    public function getModules()
    {
        return $this->modules;
    }
}

$moduleRunnerObj = new ModuleRunner();
$moduleRunnerObj->init();
echo '<pre>';

//var_dump($moduleRunnerObj);

class Person
{  #第三方类
    public $name;

    function __construct($name)
    {
        $this->name = $name;
    }
}

class FtpModule extends Module
{  #用户自定义第三方Module
    private $host = "default host";
    private $user = "default user";


    public function setHost($host)
    {
//        echo $host;
        $this->host = $host;
    }

    public function setUser($user)
    {
//        echo $user;
        $this->user = $user;
    }

    public function execute()
    {
        echo "{$this->user} user {$this->host}";
    }
}

$refobj = new ReflectionClass('FtpModule');
function getSource(ReflectionClass $reflectionClass)
{
    $path = $reflectionClass->getFileName();#获取脚本文件的文件名和绝对路径
    $fpArr = file($path); //file() 函数 获取文件的内容 把整个文件读入一个数组中 数组的每一个元素保存一行的内容。
    $start = $reflectionClass->getStartLine();
    $end = $reflectionClass->getEndLine();
    $array = array_slice($fpArr, $start - 1, $end - $start + 1);#拼装类源码
    return $array;
}

var_dump(getSource($refobj));
//echo '<pre>';
////var_dump($refobj->getMethods());
//foreach ($refobj->getMethods() as $methodObj) {
//    $argsObj=$methodObj->getParameters();
//    if (!empty($argsObj)) {
//        var_dump($argsObj[0]->getclass());
//    }
//
//}

class PersonModule extends Module
{  #用户自定义第三方Module
    private $person;

    public function setPerson(Person $person)
    {

        $this->person = $person;
    }

    public function execute()
    {
        if (isset($person)) {
            echo "I am {$this->person->name}";
        } else {
            echo "I am no user";
        }
    }
}

//$refobj = new ReflectionClass('PersonModule');
//echo '<pre>';
////var_dump($refobj->getMethods());
//foreach ($refobj->getMethods() as $method) {
//    $argsObj = $method->getParameters();
//    if (!empty($argsObj)) {
//        var_dump($argsObj[0]->getClass());
//    }
//}